#pragma once
#include<iostream>
#include<functional>
#include<unordered_map>
#include<vector>
#include<string>
#include<cstring>
#include<cerrno>
#include<cstdio>
#include<pthread.h>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"error.hpp"
#include"RingQueue.hpp"
#include"lockGuard.hpp"
#include"Thread.hpp"

//多线程版的服务器，一个线程收消息，一个线程发消息
namespace ns_server
{
    //返回值是string，参数是string的函数
    using func_t = std::function<std::string (std::string)>;
    class UdpServer
    {
    public:
        UdpServer(int port):_port(port)
        {
            std::cout<<"server addr:"<<_port<<std::endl;
            pthread_mutex_init(&_lock,nullptr);

            r=new Thread(1,std::bind(&UdpServer::Revc,this));
            c=new Thread(1,std::bind(&UdpServer::Broadcast,this));
        }

        //启动服务器
        void Start()
        {
            //1. 创建套接字(实际是打开网络文件)
            //AF_INET表示网络通信，SOCK_DGRAM是UDP的数据报，第三个参数默认为0
            _sock=socket(AF_INET,SOCK_DGRAM,0);//_sock=3
            if(_sock<0)//_sock<0代表创建套接字失败
            {
                std::cerr<<"socket create fail:"<<strerror(errno)<<std::endl;
                exit(SOCKET_ERR);
            }
            //到此代表创建套接字成功
            std::cout<<"socket create success:"<<_sock<<std::endl;

            //2.绑定结构体   sockaddr_in网络结构体
            struct sockaddr_in local;
            bzero(&local,sizeof(local));//清空结构体
            
            local.sin_family=AF_INET;
            local.sin_port=htons(_port);//转大端

            //inet_addr
            //(1).既可以转大端
            //(2).又可以字符串转in_addr_t
            //local.sin_addr.s_addr=inet_addr(_ip.c_str());

            local.sin_addr.s_addr=INADDR_ANY;//可以接收任意IP地址

            //第一个参数是套接字，第二个参数是套接字结构体，第三个是结构体大小
            int n=bind(_sock,(struct sockaddr*)&local,sizeof(local));
            if(n<0)
            {
                std::cerr<<"bind fail:"<<strerror(errno)<<std::endl;
                exit(BIND_ERR);
            }
            std::cout<<"bind success:"<<_sock<<std::endl;

            r->run();
            c->run();
        }

        //添加新用户
        void addUser(const std::string name,const struct sockaddr_in&peer)
        {
            lockGuard lg(&_lock);
            onlineUser[name]=peer;
        }

        //收消息
        void Revc()
        {
            char buffer[2048];
            while(true)
            {
                //用户的套接字，通信需要记录是谁发的
                struct sockaddr_in peer;
                memset(&peer,0,sizeof(peer));
                socklen_t len=sizeof(peer);

                //1.收信息
                //第一参数是网络文件描述符，第二参数是缓冲区，第三参数是缓冲区大小，预留一个/0
                //第四参数是阻塞读，第五参数是用户的套接字，要通信
                int n=recvfrom(_sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
                if(n<0)
                    continue;
                else 
                    buffer[n]='\0';
                
                std::cout<<peer.sin_addr.s_addr<<" # "<<buffer<<std::endl;

                //添加新用户
                std::string clientIp=inet_ntoa(peer.sin_addr);
                uint16_t clientPort=ntohs(peer.sin_port);

                std::string name=clientIp;
                name += "-";
                name += std::to_string(clientPort);
                addUser(name,peer);

                name+=">>> ";
                name+=buffer;
                
                //消息入队列
                rq.push(name);
            }
        }

        //发消息
        void Broadcast()
        {
            while(true)
            {
                std::string message;
                rq.pop(message);

                std::vector<struct sockaddr_in>v;
                {
                    lockGuard lg(&_lock);
                    for(auto&cur:onlineUser)
                        v.push_back(cur.second);
                }

                //发消息
                for(auto&cur:v)
                    sendto(_sock,message.c_str(),message.size(),0,(struct sockaddr*)&cur,sizeof(cur));
            }
        }

        ~UdpServer()
        {
            pthread_mutex_destroy(&_lock);
            c->join();
            r->join();
        }
        
    private:
        int _sock;//套接字
        uint16_t _port;//端口号
        RingQueue<std::string>rq;
        std::unordered_map<std::string,struct sockaddr_in>onlineUser;
        pthread_mutex_t _lock;
        Thread *r;
        Thread *c;
    };
}